#ifndef     __SPI_FLASH_H
#define     __SPI_FLASH_H

#include <stdint.h>
#include <string.h>
#include "../utils/md5.h"

namespace driver {

namespace spi_flash {

void init();

void read(uint32_t addr, void *buf, uint32_t len);
void write(uint32_t addr, const void *buf, uint32_t len);
uint16_t get_id();

}

namespace store_impl {

template <typename M>
struct obj_t {
    M m;
    uint8_t md5[16];
};

}

template <uint32_t Addr, typename M>
struct store_t {
public:
    inline M* operator->() {
        return &m_obj.m;
    }

    void load() {
        uint8_t dest[16];
        spi_flash::read(Addr, &m_obj, sizeof (m_obj));
        utils::md5::Sum(&m_obj.m, sizeof (m_obj.m), dest);
        if (0 != memcmp(dest, m_obj.md5, 16)) {
            m_obj.m.reset();
        }
    }

    void save() {
        utils::md5::Sum(&m_obj.m, sizeof (m_obj.m), m_obj.md5);
        spi_flash::write(Addr, &m_obj, sizeof (m_obj));
    }

private:
    store_impl::obj_t<M> m_obj;
};

constexpr uint32_t DeviceInfoAddr = 0;
constexpr uint32_t TubeAddr = 4096;
constexpr uint32_t FridgeAddr = 4096 * 2;
constexpr uint32_t RobotArm1Addr = 4096 * 3;
constexpr uint32_t RobotArm2Addr = 4096 * 4;

}


#endif
